
// 1、将 data 的所有子孙属性注入 getter、setter
// 2、在数据变化时，也就是 setter 里面触发 dep.notify()
// 3、重点是在哪儿向 dep 添加订阅 dep.addSub() 比较合适？
// 3.1、在页面第一次使用数据时，也就是第一次调用 getter 的时候（理论上是在 getter 里面调用 dep.addSub()）
// 3.2、而且 getter 里面调用 dep.addSub()）只能是一次，不能重复；所以传值过后标记需要置空（callback 实际已经放到了 Watcher 的示例中）；
// 3.3、watcher 里面 update() 的功能就是更新视图，所以 watcher 需要有一个 callback 用于处理更新视图；
// 3.4、方案一：但是 observe 中拿不到编译视图的方法与参数，所以需要挂到 vm 实例上；然后在 getter 里面直接从 vm 实例上取 callback；
// 3.5、方案二：
// （1）将 new Watcher() 放到 compile.js 里面，即可直接使用对应的 node 和数据；
// （2）然后在 Watcher 类的 constructor 里面将实例自己挂到 Dep类上作为标记(例如：Dep.target = this)；
// （3）然后在 Watcher 类的 constructor 里面再次触发 getter（例如：this.oldValue = vm.$data[key]）
// （4）在 getter 里面通过 dep.addSubs(Dep.target) 添加  Watcher 实例；


// 因为在 Compile 的时候，会是第一次使用数据的 getter，所以可以将 dep.addSub() 的触发条件放到 Compile 里面
import Dep from "./dep.js";
import Watcher from "./watcher.js";

export default class Observe {
    constructor(vm) {
        let data = vm.$data
        this.vm = vm
        this.walk(data)
    }

    walk(data) {
        if(data && typeof data === "object"){
            Object.keys(data).forEach(key => {
                this.defineReactive(data, key, data[key])
            })
        }
    }

    defineReactive(data, key, value) {
        // 如果 value 也是一个对象，递归执行绑定该对象的子属性
        this.walk(value);

        let dep = new Dep()
        let that = this
        Object.defineProperty(data, key, {
            get() {
                // 注意：只在首次赋值时添加订阅dep.addSub()
                that.vm.$callback[key] && dep.addSub(new Watcher(that.vm, key, value))
                return value
            },

            set(newValue) {
                if(newValue !== value){
                    value = newValue
                    dep.notify()
                }
            }
        })
    }
}